package edu.whut.mall.portal.service.impl;

import edu.whut.mall.common.api.CommonResult;
import edu.whut.mall.common.constant.MallConstant;
import edu.whut.mall.common.exception.MallException;
import edu.whut.mall.common.util.JsonUtil;
import edu.whut.mall.mapper.MerchantMapper;
import edu.whut.mall.mapper.PayInfoMapper;
import edu.whut.mall.mapper.RefundRequestMapper;
import edu.whut.mall.model.*;
import edu.whut.mall.portal.domain.GroupBuyOrderParams;
import edu.whut.mall.portal.domain.PayInfoParams;
import edu.whut.mall.portal.dto.GroupBuyQualificationResult;
import edu.whut.mall.portal.dto.MerchantPayInfo;
import edu.whut.mall.portal.enums.*;
import edu.whut.mall.portal.mapper.GroupBuyOrderCustomMapper;
import edu.whut.mall.portal.service.IGroupBuyOrderService;
import edu.whut.mall.portal.service.IPayInfoService;
import edu.whut.mall.portal.service.IPortalGroupBuyService;
import edu.whut.mall.portal.vo.GroupBuyOrderVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pay.d9lab.api.model.BaseResponseModule;
import pay.d9lab.api.model.NotifyModel;
import pay.d9lab.api.model.PrepayRequestModel;
import pay.d9lab.api.model.RefundRequestModel;
import pay.d9lab.api.util.Api;

import javax.servlet.http.HttpServletRequest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
* @program: d9lab-mall
* @description: 支付信息 Service实现
* @author Wayne
* @date 2020-05-22 16:16:25
*/

@Service
@Slf4j
@RequiredArgsConstructor
public class PayInfoServiceImpl implements IPayInfoService {


    private final PayInfoMapper payInfoMapper;

    private final MerchantMapper merchantMapper;

    private final GroupBuyOrderCustomMapper groupBuyOrderCustomMapper;

    private final IGroupBuyOrderService groupBuyOrderService;

    private final RefundRequestMapper refundRequestMapper;

    private IPortalGroupBuyService portalGroupBuyService;

    @Autowired
    public void setPortalGroupBuyService(IPortalGroupBuyService portalGroupBuyService) {
        this.portalGroupBuyService = portalGroupBuyService;
    }

    @Override
    public CommonResult Create(PayInfoParams payInfoParams) throws NoSuchAlgorithmException {
        log.info("{} ####################预支付开始#####################", MallConstant.PAY_LOG_PREFIX);
        log.info("{} 本次订单请求信息：{}", MallConstant.PAY_LOG_PREFIX, payInfoParams);
        //决定支付的方式
        MerchantPayInfo merchantPayInfo = getMerchantPayInfoByMerchantIdAndProcessDirection(payInfoParams.getMerchantId(), null);
        log.info("{} 本次订单merchant信息：{}", MallConstant.PAY_LOG_PREFIX, merchantPayInfo);
        //订单入库
        GroupBuyOrderVo orderVo = groupBuyOrderService.createGroupBuyOrder(payInfoParams);
        //开始进行支付请求
        //封装参数
        Map<String, Object> map = new PrepayRequestModel()
                .platform_id(merchantPayInfo.getPlatformId())
                .pay_platform("WxPay")
                .trade_type("JSAPI")
                .out_trade_no(orderVo.getOrderNo())
                .title(payInfoParams.getProductName())
                .total_amount(payInfoParams.getAmount().toString())
                .notify_url(MallConstant.NOTIFY_URL)
                .open_id(payInfoParams.getOpenId())
                .creatSign(merchantPayInfo.getPlatformKey());//生成随机字符串并加签
        log.info("{} 本次订单封装map信息：{}", MallConstant.PAY_LOG_PREFIX, map);
         //请求
        String response = Api.prepay(map);
        //接参
        BaseResponseModule responseModule = new BaseResponseModule(response);
        if (!responseModule.isSuccess()) {
            log.info("{} 本次订单预付失败信息：{}", MallConstant.PAY_LOG_PREFIX, responseModule.getMessage());
            return null;
        }
        //获取结果键值对
        Map<String, Object> valueMap = responseModule.getValueMap();
        log.info("{} 本次订单预付信息：{}", MallConstant.PAY_LOG_PREFIX, valueMap);
        //获取prepay_id
        String prepayId = ((String)valueMap.get("package")).split("=")[1];
        //支付信息入库
        PayInfo payInfo = buildPayInfo(payInfoParams, orderVo.getOrderNo(), merchantPayInfo.getProceedsFlowDirection(), prepayId);
        payInfoMapper.insertSelective(payInfo);
        log.info("{} ####################预支付结束#####################", MallConstant.PAY_LOG_PREFIX);
        return CommonResult.success(valueMap);
    }

    /**
     * 根据前端参数来拼出订单
     * @param payInfoParams
     * @return
     */
    private PayInfo buildPayInfo(PayInfoParams payInfoParams, String orderNo, Integer proceedsFlowDirection, String prepayId) {
        PayInfo payInfo = new PayInfo();
        BeanUtils.copyProperties(payInfoParams, payInfo);
        payInfo.setPrepayId(prepayId);
        payInfo.setOrderNo(orderNo);
        payInfo.setPlatformStatus(PaymentStatusEnum.NO_PAY.getCode());
        payInfo.setProceedsFlowDirection(proceedsFlowDirection);
        payInfo.setAttach(JsonUtil.obj2Str(GroupBuyOrderParams.extractGroupOrderAttach(payInfoParams)));
        return payInfo;
    }

    @Override
    public String asyncNotify(HttpServletRequest request) throws NoSuchAlgorithmException {
        log.info("{} ####################开始异步回调#####################", MallConstant.PAY_LOG_PREFIX);
        NotifyModel notifyModel = new NotifyModel(request);
        //查找对应的companyKey
        String orderNo = notifyModel.getOutTradeNo();
        PayInfo payInfo = queryPayInfoByOrderNo(orderNo);
        //决定支付的方式
        MerchantPayInfo merchantPayInfo = getMerchantPayInfoByMerchantIdAndProcessDirection(payInfo.getMerchantId(), payInfo.getProceedsFlowDirection());
        //验签
        notifyModel.verifyRequest(request, merchantPayInfo.getPlatformKey());
        if (notifyModel.isTrustedRequest()) {
            log.info("{}:异步返回信息成功！！！{}", MallConstant.PAY_LOG_PREFIX, JsonUtil.obj2Str(notifyModel));
        } else {
            log.info("{}:异步返回信息失败！！！{}", MallConstant.PAY_LOG_PREFIX, JsonUtil.obj2Str(notifyModel));
            return MallConstant.PAY_FAIL_RETURN_INFO;
        }
        GroupBuyOrderParams groupBuyOrderParams = JsonUtil.str2Obj(payInfo.getAttach(), GroupBuyOrderParams.class);
        log.info("{}:异步返回团购信息{}", MallConstant.PAY_LOG_PREFIX, groupBuyOrderParams);
        GroupBuyQualificationResult result;
        if (GroupBuyUserTypeEnum.GROUP_BUY_CREATE.getCode().equals(groupBuyOrderParams.getUserType())) {
            result = portalGroupBuyService.createGroupBuyRecord(payInfo.getUserId(), groupBuyOrderParams.getGroupBuyRuleId(), orderNo);
        } else {
            result = portalGroupBuyService.joinGroupBuyRecord(payInfo.getUserId(), groupBuyOrderParams.getGroupBuyRecordId(), orderNo);
        }
        log.info("{} 开团的结果：{}", MallConstant.PAY_LOG_PREFIX, result);
        log.info("{} ####################异步回调结束#####################", MallConstant.PAY_LOG_PREFIX);
        if (result.isQualified()) {
            updateGroupBuyOrderStatus(orderNo, OrderStatusEnum.PAID.getCode());
            updatePayInfo(orderNo, payInfo.getPlatformNumber(), PaymentStatusEnum.PAID.getCode());
        }
        return MallConstant.PAY_SUCCESS_RETURN_INFO;
    }

    @Override
    public PayInfo queryPayInfoByOrderNo(String orderNo) {
        PayInfoExample payInfoExample = new PayInfoExample();
        payInfoExample.createCriteria().andOrderNoEqualTo(orderNo);
        List<PayInfo> payInfos = payInfoMapper.selectByExample(payInfoExample);
        return CollectionUtils.isEmpty(payInfos)? null : payInfos.get(0);
    }

    @Override
    public CommonResult refund(String orderNo) throws NoSuchAlgorithmException {
        log.info("{}####################开始退款#####################", MallConstant.PAY_LOG_PREFIX);
        PayInfo payInfo = queryPayInfoByOrderNo(orderNo);
        //决定支付的方式
        MerchantPayInfo merchantPayInfo = getMerchantPayInfoByMerchantIdAndProcessDirection(payInfo.getMerchantId(), payInfo.getProceedsFlowDirection());
        //封装参数
        Map<String,Object> map = new RefundRequestModel()
                .platform_id(merchantPayInfo.getPlatformId())
                .out_trade_no(orderNo)
                .platform_out_trade_no(payInfo.getPlatformNumber())
                .refund_amount(payInfo.getAmount().toString())
                .creatSign(merchantPayInfo.getPlatformKey());
                //生成随机字符串并加签
        //请求
        String response = Api.refund(map);
        //接参
        BaseResponseModule responseModule = new BaseResponseModule(response);
        //获取结果键值对
        Map<String,Object> valueMap = responseModule.getValueMap();
        if ((boolean)(valueMap.get("success"))) {
            log.info("{} 退款结果{}", MallConstant.PAY_LOG_PREFIX, valueMap);
            log.info("{}####################退款结束---成功#####################", MallConstant.PAY_LOG_PREFIX);
            return CommonResult.success(valueMap);
        } else {
            log.info("{}####################退款结束---失败#####################{}", MallConstant.PAY_LOG_PREFIX, (String)valueMap.get("message"));
            log.info("{}失败原因：{}", MallConstant.PAY_LOG_PREFIX, valueMap.get("message"));
            log.info("{}####################退款结束#####################", MallConstant.PAY_LOG_PREFIX);
            return CommonResult.failed((String)(valueMap.get("message")));
        }

    }


    /**
     * 付款后更新团购状态
     * @param orderNo
     */
    @Override
    public void updateGroupBuyOrderStatus(String orderNo, Integer orderStatusCode){
        GroupBuyOrder order = groupBuyOrderCustomMapper.queryGroupBuyOrderByOrderNo(orderNo);
        if (order == null){
            throw new MallException("订单不存在");
        }
        Date now = new Date();
        //如果是付款则必须更新paymentTime,其他操作不需要更新
        if (OrderStatusEnum.PAID.getCode().equals(orderStatusCode)) {
            order.setPaymentTime(now);
        }
        order.setUpdateTime(now);
        order.setPaymentType(PaymentTypeEnum.WX.getCode());
        order.setOrderStatus(orderStatusCode);
        groupBuyOrderCustomMapper.updateGroupBuyOrderByOrderNo(order);
    }

    /**
     * 更新支付信息
     * @param orderNo
     */
    public void updatePayInfo(String orderNo, String platformNumber, Integer PaymentStatusCode) {
        PayInfo payInfo = new PayInfo();
        payInfo.setPlatformStatus(PaymentStatusCode);
        payInfo.setUpdateTime(new Date());
        payInfo.setOrderNo(orderNo);
        payInfo.setPlatformNumber(platformNumber);
        groupBuyOrderCustomMapper.updatePayInfoStatusByOrderNo(payInfo);
    }

    @Override
    public void updatePayInfoStatus(String orderNo, Integer PaymentStatusCode) {
        PayInfo payInfo = new PayInfo();
        payInfo.setPlatformStatus(PaymentStatusCode);
        payInfo.setUpdateTime(new Date());
        payInfo.setOrderNo(orderNo);
        groupBuyOrderCustomMapper.updatePayInfoStatusByOrderNo(payInfo);
    }

    @Override
    @Transactional
    public Integer requestFund(String orderNo, String reason) {
        GroupBuyOrder order = groupBuyOrderCustomMapper.queryGroupBuyOrderByOrderNo(orderNo);
        PayInfo payInfo = queryPayInfoByOrderNo(orderNo);
        RefundRequest refundRequest = new RefundRequest();
        refundRequest.setAmount(payInfo.getAmount());
        refundRequest.setOrderNo(orderNo);
        refundRequest.setProductId(order.getProductId());
        refundRequest.setUserId(order.getUserId());
        refundRequest.setReason(reason);
        refundRequest.setStatus(RefundStatusEnum.UNDER_REVIEW.getCode());
        refundRequest.setRefundSource(payInfo.getProceedsFlowDirection());
        //更新支付状态
        updatePayInfoStatus(orderNo, PaymentStatusEnum.WAIT_REFUND.getCode());
        //更新订单状态
        updateGroupBuyOrderStatus(orderNo, OrderStatusEnum.WAIT_REFUND.getCode());
        return refundRequestMapper.insertSelective(refundRequest);
    }

    /**
     * 根据商户id和金钱流向来决定platformId和platformKey
     * @param merchantId
     * @param direction
     * @return
     */
    private MerchantPayInfo getMerchantPayInfoByMerchantIdAndProcessDirection(Long merchantId, Integer direction) {
        MerchantPayInfo merchantPayInfo = new MerchantPayInfo();
        String platformId, platformKey;
        Merchant merchant = merchantMapper.selectByPrimaryKey(merchantId);
        Integer currentDirection;
        if (direction == null) {
            currentDirection = merchant.getProceedsFlowDirection();
        } else {
            currentDirection = direction;
        }
        if (ProceedsFlowDirectionEnum.COMMON.getCode().equals(currentDirection)) {
            platformId = MallConstant.DEFAULT_PLATFORM_ID;
            platformKey = MallConstant.DEFAULT_PLATFORM_KEY;
        } else {
            platformId = merchant.getMerchantWxpayAccount();
            platformKey = merchant.getCompanyKey();
            if (StringUtils.isBlank(platformId) || StringUtils.isBlank(platformKey)) {
                log.error("商家id:{} 选择了独家支付但是没有appId", merchant.getId());
                throw new MallException("商家选择了独家支付但是没有appId");
            }
        }
        merchantPayInfo.setPlatformId(platformId);
        merchantPayInfo.setPlatformKey(platformKey);
        merchantPayInfo.setProceedsFlowDirection(currentDirection);
        return merchantPayInfo;
    }


}



